import { Injectable } from '@nestjs/common';
import { cmdExec } from '../utils';
import { deviceInfoCmd } from './cmd';
import { Device } from './entity/device.entity';
import { getCache } from './device.cache';

class DeviceInfo implements Device {
  id: string;
  link_type: string;
  brand: string;
  device_name: string;
  device_type: string;
  os_version: string;
  cpu_type: string;
  cpu_arch: string;
  cpu_core_num: number;
  cpu_cluster_num: number;
  cpu_cluster_name: string[];
  cpu_cluster_cores: number[][];
  cpu_cluster_max_freq: number[];
  cpu_cluster_min_freq: number[];
  gpu_type: string;
  gpu_max_freq: number;
  gpu_min_freq: number;
  gl_version: string;
  vulkan_version: string;
  resolution: string;
  usb_id: string;
  wlan_ip: string;
  is_full_info: boolean;
  all_info_ready: boolean;
  constructor(id: string) {
    this.id = id;
    this.link_type = '';
    this.brand = '';
    this.device_name = '';
    this.device_type = '';
    this.os_version = '';
    this.cpu_type = '';
    this.cpu_arch = '';
    this.cpu_core_num = 0;
    this.cpu_cluster_num = 0;
    this.cpu_cluster_name = [];
    this.cpu_cluster_cores = [];
    this.cpu_cluster_max_freq = [];
    this.cpu_cluster_min_freq = [];
    this.gpu_type = '';
    this.gpu_max_freq = 0;
    this.gpu_min_freq = 0;
    this.gl_version = '';
    this.vulkan_version = '';
    this.resolution = '';

    /* used to switch */
    this.usb_id = '';
    this.wlan_ip = '';

    this.is_full_info = false;
    this.all_info_ready = true;
  }
}

const getBaseInfo = async (SN: string, info: Device) => {
  let cmd_ret: string[];
  try {
    cmd_ret = await Promise.all([
      cmdExec(deviceInfoCmd.hdc.getDeviceInfo(SN, 'const.product.cpu.abilist')),
      cmdExec(deviceInfoCmd.hdc.getDeviceInfo(SN, 'const.product.name')),
      cmdExec(deviceInfoCmd.hdc.getDeviceInfo(SN, 'const.product.model')),
      cmdExec(deviceInfoCmd.hdc.getDeviceInfo(SN, 'const.ohos.fullname')),
      cmdExec(deviceInfoCmd.hdc.getDeviceInfo(SN, 'ohos.boot.hardware')),
    ]);
    cmd_ret = cmd_ret.map((item) => item.replace(/\r?\n/, '').trim());
    info.cpu_arch = cmd_ret[0];
    info.device_name = cmd_ret[1];
    info.device_type = cmd_ret[2];
    info.os_version = cmd_ret[3];
    info.cpu_type = cmd_ret[4];
    info.brand = 'HUAWEI';
  } catch (err) {
    info.all_info_ready = false;
  }
};

const getClusterCores = async (info: Device, devId: string) => {
  let core_num = 0;
  let affected_num = 0;
  let cmd_ret = [];
  // 核对 core_num 信息是否正确
  try {
    cmd_ret = await cmdExec(deviceInfoCmd.hdc.getProcStat(devId)).then(
      (res) => {
        return res.toString().split('\r\n').filter(Boolean);
      },
    );
    cmd_ret.forEach((line) => {
      if (line.startsWith('cpu')) {
        core_num++;
      }
    });
    // Remove first cpu line
    core_num--;
    if (core_num != info.cpu_core_num) {
      console.log('Update cpu core num from proc stat');
      info.cpu_core_num = core_num;
    }
  } catch (err) {
    console.log('Get proc stat data error');
    core_num = info.cpu_core_num;
  }
  // 通过 affected_cpus 信息获取设备集群对应cpu信息
  let promiseList = [];
  for (let i = 0; i < info.cpu_cluster_num; i++) {
    promiseList.push(
      (async () => {
        try {
          // 设备集群对应cpu信息 policy0 --> [0, 1, 2, 3]
          cmd_ret = await cmdExec(
            deviceInfoCmd.hdc.getAffectedCpus(devId, info.cpu_cluster_name[i]),
          ).then((res) => {
            return res.toString().split('\r\n').filter(Boolean);
          });
          const cores = cmd_ret[0].split(' ').filter(Boolean);
          info.cpu_cluster_cores.push(
            cores.map((core: string) => parseInt(core)),
          );
          affected_num += cores.length;
        } catch (err) {
          console.log('Load cluster core from affected cpus failed');
        }
      })(),
    );
  }
  await Promise.all(promiseList);
  if (affected_num === core_num) {
    console.log('Load cpu policy info from affected OK.');
    return;
  }
  // 通过 cpu_policy 信息获取设备集群对应cpu信息
  let target_cluster_num = 0;
  info.cpu_cluster_cores = [];
  for (let j = 0; j < info.cpu_cluster_num; j++) {
    info.cpu_cluster_cores.push([]);
  }
  promiseList = [];
  for (let i = 0; i < core_num; i++) {
    promiseList.push(
      (async () => {
        try {
          cmd_ret = await cmdExec(
            deviceInfoCmd.hdc.getCpuPolicy(devId, i),
          ).then((res) => {
            return res.toString().split('\r\n').filter(Boolean);
          });

          const pos = cmd_ret[0].indexOf('policy');
          if (pos < 0) {
            console.log('Load cpu policy info error');
            return;
          }
          const target_cluster = cmd_ret[0].substr(pos);
          for (let j = 0; j < info.cpu_cluster_num; j++) {
            if (target_cluster === info.cpu_cluster_name[j]) {
              info.cpu_cluster_cores[j].push(i);
              target_cluster_num++;
            }
          }
        } catch (err) {
          console.log('Load cpu policy info error');
        }
      })(),
    );
  }
  await Promise.all(promiseList);
  if (target_cluster_num !== core_num) {
    console.log('Load cpu policy info from cpu freq failed.');
    info.cpu_cluster_cores = [];
  } else {
    console.log('Load cpu policy info from cpu freq OK.');
  }
};

const getCpuInfo = async (SN: string, info: Device) => {
  try {
    let cmd_ret: string | string[];
    // 获取设备核数 8 12
    cmd_ret = await cmdExec(deviceInfoCmd.hdc.getCPUCoreNum(SN));
    info.cpu_core_num = parseInt(cmd_ret);
    // 获取设备集群信息 [policy0, policy1, policy2]
    cmd_ret = (await cmdExec(deviceInfoCmd.hdc.getCPUFreqPolicy(SN)).then(
      (res) => {
        return res.toString().split('\r\n').filter(Boolean);
      },
    )) as string[];
    info.cpu_cluster_num = cmd_ret.length;
    // 获取集群 maxFreq 和 minFreq
    const promiseList = [];
    cmd_ret.forEach((line) => {
      promiseList.push(
        (async () => {
          let freq = await cmdExec(deviceInfoCmd.hdc.getMaxCPUFreq(SN, line));
          info.cpu_cluster_max_freq.push(parseInt(freq as string));
          freq = await cmdExec(deviceInfoCmd.hdc.getMinCPUFreq(SN, line));
          info.cpu_cluster_min_freq.push(parseInt(freq as string));
          info.cpu_cluster_name.push(line);
        })(),
      );
    });
    await Promise.all(promiseList);
    await getClusterCores(info, SN);
  } catch (err) {
    info.all_info_ready = false;
  }
};

const getGpuInfo = async (SN: string, info: Device) => {
  try {
    let cmd_ret: string;
    cmd_ret = await cmdExec(deviceInfoCmd.hdc.getGPUMaxFreq(SN));
    info.gpu_max_freq = parseInt(cmd_ret);
    cmd_ret = await cmdExec(deviceInfoCmd.hdc.getGPUMinFreq(SN));
    info.gpu_min_freq = parseInt(cmd_ret);
  } catch (err) {
    info.all_info_ready = false;
  }
};

const cacheInfoForList = new Map<string, Device>();
const cacheInfo = new Map<string, Device>();

export const getDeviceInfoByHdc = async (SN: string, isListInfo: boolean) => {
  if (SN && isListInfo && cacheInfoForList.has(SN))
    return cacheInfoForList.get(SN);
  if (SN && cacheInfo.has(SN)) return cacheInfo.get(SN);
  const info = new DeviceInfo(SN);
  info.link_type = 'hdc';
  await getBaseInfo(SN, info);
  if (isListInfo) {
    cacheInfoForList.set(SN, { ...info });
    return info;
  }
  info.is_full_info = true;
  const cache = getCache(info.device_name, info.device_type);
  if (cache && Object.keys(cache).length) {
    Object.assign(info, { ...cache });
  } else {
    await Promise.all([getCpuInfo(SN, info), getGpuInfo(SN, info)]);
  }
  cacheInfo.set(SN, { ...info });
  return info;
};

@Injectable()
export class DeviceService {
  async getDeviceInfo(SN: string, isListInfo: boolean): Promise<Device> {
    return getDeviceInfoByHdc(SN, isListInfo);
  }
  clearDeviceInfo(SN: string) {
    cacheInfoForList.delete(SN);
    cacheInfo.delete(SN);
  }
}
